package com.atguigu.tingshu.account.listener;

import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.constant.TingShuMqConstants;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.rabbitmq.client.Channel;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Component
public class AccountMqListener {
    @Resource
    private UserAccountService userAccountService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private UserAccountDetailMapper userAccountDetailMapper;

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = TingShuMqConstants.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            value = @Queue(value = TingShuMqConstants.ACCOUNT_DEDUCT_QUEUE),
            key = TingShuMqConstants.ACCOUNT_DEDUCT_RK
    ))   ///扣减余额
    public void deductionAmountListener(String orderNo, Message message, Channel channel) throws IOException {
        if (StringUtils.isBlank(orderNo)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        //保证幂等性
        String idemKey = RedisConstant.ACCOUNT_DEDUCT_IDEM_PREFIX + orderNo;
        Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(idemKey, "", RedisConstant.IDEM_TIMEOUT, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(flag)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        try {
            /// 处理业务
            this.userAccountService.deductionAmount(orderNo);
            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            this.redisTemplate.delete(idemKey);
            if (message.getMessageProperties().getRedelivered()) {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = TingShuMqConstants.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            value = @Queue(value = TingShuMqConstants.ACCOUNT_UNLOCK_QUEUE),
            key = {TingShuMqConstants.ACCOUNT_UNLOCK_RK, TingShuMqConstants.ACCOUNT_DEAD_RK}
    ))   ///解锁余额
    public void unlockAmountListener(String orderNo, Message message, Channel channel) throws IOException {
        if (StringUtils.isBlank(orderNo)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        //保证幂等性
        String idemKey = RedisConstant.ACCOUNT_UNLOCK_IDEM_PREFIX + orderNo;
        Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(idemKey, "", RedisConstant.IDEM_TIMEOUT, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(flag)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        try {
            /// 处理业务
            //获取redis缓存的锁定信息
            String infoKey = RedisConstant.ACCOUNT_LOCK_INFO_PREFIX + orderNo;
            AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(infoKey);
            if (accountLockResultVo == null) {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            BigDecimal amount = accountLockResultVo.getAmount();
            Long userId = accountLockResultVo.getUserId();
            //解锁账户锁定余额并保存
            int row = this.userAccountService.unlockAccount(userId, amount);
            if (row == 0) {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            //记录账户变化的信息
            this.userAccountDetailMapper.insert(new UserAccountDetail().setUserId(userId)
                    .setTitle("余额解锁").setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK)
                    .setAmount(amount).setOrderNo(orderNo));
            //删除锁定缓存
            this.redisTemplate.delete(infoKey);
            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            this.redisTemplate.delete(idemKey);
            if (message.getMessageProperties().getRedelivered()) {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = TingShuMqConstants.TING_SHU_USER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            value = @Queue(value = TingShuMqConstants.QUEUE_USER_REGISTER),
            key = TingShuMqConstants.TING_SHU_USER_ROUTING_KEY
    )) //初始化账户信息
    public void userMqListener(Long userId, Message message, Channel channel) throws IOException {
        if (userId == null) {
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        try {
            // 处理业务
            userAccountService.initUserAccount(userId);
            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            if (message.getMessageProperties().getRedelivered()) {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = TingShuMqConstants.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            value = @Queue(value = TingShuMqConstants.ACCOUNT_REFUND_QUEUE),
            key = TingShuMqConstants.ACCOUNT_REFUND_RK
    )) //初始化账户信息
    public void refundMqListener(String orderNo, Message message, Channel channel) throws IOException {
        if (StringUtils.isBlank(orderNo)) {
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        String itemKey = RedisConstant.ACCOUNT_REFUND_IDEM_PREFIX + orderNo;
        Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(itemKey, "1", RedisConstant.ITEM_TIMEOUT, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(flag)) {
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        try {
            this.userAccountService.refund(orderNo);
            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            if (message.getMessageProperties().getRedelivered()) {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }
}
